package com.thx;

import com.thx.common.Constant;
import com.thx.user.DatabaseAuthenticationProvider;
import com.thx.web.filter.WebSessionFilter;

import org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.core.env.SimpleCommandLinePropertySource;
import org.springframework.orm.hibernate4.support.OpenSessionInViewFilter;
import org.springframework.orm.jpa.vendor.HibernateJpaSessionFactoryBean;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Properties;

import javax.inject.Inject;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;

@SpringBootApplication
public class AuditWebApplication implements ServletContextInitializer {

  private static final Logger log = LoggerFactory.getLogger(AuditWebApplication.class);

  @Inject
  private Environment env;

  /**
   * 嵌入 Tomcat 服务器运行入口.
   */
  public static void main(String[] args) throws UnknownHostException {
    SpringApplication app = new SpringApplication(AuditWebApplication.class);

    // 直接启动时如果没有设定 profile, 给一个 dev 的环境
    SimpleCommandLinePropertySource source = new SimpleCommandLinePropertySource(args);
    addDefaultProfile(app, source);

    Environment env = app.run(args).getEnvironment();
    String contextPath = env.getProperty("server.contextPath") != null
        ? env.getProperty("server.contextPath") : "";
    String port = env.getProperty("server.port") != null ? env.getProperty("server.port") : "8080";

    log.info("服务器地址列表:\n----------------------------------------------------------\n"
            + "\t本地地址: \thttp://localhost:{}{}\n"
            + "\t其他机器: \thttp://{}:{}{}\n----------------------------------------------------------",
        port, contextPath,
        InetAddress.getLocalHost().getHostAddress(),
        port, contextPath);
  }


  @Override
  public void onStartup(ServletContext servletContext) throws ServletException {

    if (env == null) {
      log.info("开始 Web 应用配置, profiles: None");
    } else {
      log.info("开始 Web 应用配置, profiles: {}", Arrays.toString(env.getActiveProfiles()));
    }

    EnumSet<DispatcherType> disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD,
        DispatcherType.ASYNC);

    // 初始化 OpenSessionInView
    initOpenSessionInViewFilter(servletContext, disps);

    //初始化 WebSessionFilter
    initWebSessionFilter(servletContext, disps);

    // 初始化 struts2
    initStruts2Filter(servletContext, disps);


    log.info("Web 应用已配置完成");
  }

  /**
   * 设定 Struts2 过滤器.
   */
  private void initStruts2Filter(ServletContext servletContext, EnumSet<DispatcherType> disps) {
    log.info("初始化 Struts2 过滤器");
    FilterRegistration.Dynamic filter = servletContext.addFilter("struts2",
        new StrutsPrepareAndExecuteFilter());

    filter.addMappingForUrlPatterns(disps, true, "*.action");
    filter.addMappingForUrlPatterns(disps, true, "/config-browser/*");
    filter.addMappingForUrlPatterns(disps, true, "/config-browser/");

  }

  /**
   * 设定 OpenSessionInView 过滤器.
   */
  private void initOpenSessionInViewFilter(ServletContext servletContext, EnumSet<DispatcherType> disps) {
    log.info("初始化 OpenSessionInView 过滤器");
    FilterRegistration.Dynamic filter = servletContext.addFilter("OpenSessionInViewFilter",
        new OpenSessionInViewFilter());

    filter.addMappingForUrlPatterns(disps, true, "*.action");
    filter.addMappingForUrlPatterns(disps, true, "*.jsp");
  }

  /**
   * 设定 OpenSessionInView 过滤器.
   */
  private void initWebSessionFilter(ServletContext servletContext, EnumSet<DispatcherType> disps) {
    log.info("初始化 WebSessionFilter 过滤器");
    FilterRegistration.Dynamic filter = servletContext.addFilter("sessionFilter",
        new WebSessionFilter());

    filter.setInitParameter("excludePaths",
        "login.jsp,login!input.action,login!logon.action,captcha.action,checkUserName.action,doNewHtml5New.action,doNewHtml5List.action");
    filter.setInitParameter("failGo", "failGo.html");
    filter.addMappingForUrlPatterns(disps, true, "*.action");
    filter.addMappingForUrlPatterns(disps, true, "*.jsp");
  }

  /**
   * 添加默认的环境 dev, 不过目前还没处理好在 Tomcat 容器环境下的默认 profile, 也就是说 如果是 嵌入式运行, 那么是有一个 dev 的环境, 如果是部署到 tomcat,
   * 默认是没环境(profile) 的
   */
  private static void addDefaultProfile(SpringApplication app, SimpleCommandLinePropertySource source) {
    if (!source.containsProperty("spring.profiles.active") &&
        !System.getenv().containsKey("SPRING_PROFILES_ACTIVE")) {
      app.setAdditionalProfiles(Constant.SPRING_PROFILE_DEVELOPMENT);
    }
  }

  @Bean
  public HibernateJpaSessionFactoryBean sessionFactory() {
    return new HibernateJpaSessionFactoryBean();
  }

  /**
   * 安全配置.
   */
  @EnableWebSecurity(debug = false)
  public static class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
      auth.userDetailsService(inMemoryUserDetailsManager());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
      web
          .ignoring()
          .antMatchers("/css/**/*", "/images/**/*", "/scripts/**/*", "/fonts/**/*", "/lib/**/*");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
      http
          .csrf().disable() // 允许直接提交
          .formLogin()
          .loginPage("/pages/system/common/login.jsp")
          .defaultSuccessUrl("/pages/index.jsp", false)
          .usernameParameter("username")
          .passwordParameter("passwd")
          .permitAll()
          .and()
          .authenticationProvider(authenticationProvider())
          .requestMatchers()
          .antMatchers("/**")
          .and()
          .authorizeRequests()
          .anyRequest().permitAll();
      // .authenticated(); // 临时关闭安全验证
    }

    @Bean
    public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
      final Properties users = new Properties();
      users.put("psl", "123456,ROLE_USER,enabled");
      users.put("admin", "123456,ROLE_ADMIN,enabled");
      return new InMemoryUserDetailsManager(users);
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
      return new DatabaseAuthenticationProvider();
    }
  }

}
